package com.sugar.rpc.client.proxy;

import com.sugar.rpc.common.annotation.RpcClient;
import com.sugar.rpc.common.model.RpcRequest;
import com.sugar.rpc.common.model.RpcResponse;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;

// client/RpcClientProxy.java
@Component
public class RpcClientProxy implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String name) {
        for (Field field : bean.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(RpcClient.class)) {
                field.setAccessible(true);
                Object proxy = Proxy.newProxyInstance(
                        field.getType().getClassLoader(),
                        new Class[]{field.getType()},
                        (proxyObj, method, args) -> {
                            RpcRequest req = new RpcRequest();
                            req.setInterfaceName(field.getType().getName());
                            req.setMethodName(method.getName());
                            req.setParamTypes(method.getParameterTypes());
                            req.setArgs(args);

                            // Netty client
                            EventLoopGroup group = new NioEventLoopGroup();
                            try {
                                Bootstrap bootstrap = new Bootstrap();
                                final RpcResponse[] responseHolder = new RpcResponse[1];
                                bootstrap.group(group)
                                        .channel(NioSocketChannel.class)
                                        .handler(new ChannelInitializer<SocketChannel>() {
                                            protected void initChannel(SocketChannel ch) {
                                                ChannelPipeline p = ch.pipeline();
                                                p.addLast(new ObjectEncoder());
                                                p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
                                                p.addLast(new SimpleChannelInboundHandler<RpcResponse>() {
                                                    @Override
                                                    protected void channelRead0(ChannelHandlerContext ctx, RpcResponse resp) {
                                                        responseHolder[0] = resp;
                                                        ctx.close();
                                                    }
                                                });
                                            }
                                        });

                                ChannelFuture f = bootstrap.connect("localhost", 8088).sync();
                                f.channel().writeAndFlush(req).sync();
                                f.channel().closeFuture().sync();

                                RpcResponse resp = responseHolder[0];
                                if (resp.getError() != null) throw new RuntimeException(resp.getError());
                                return resp.getResult();
                            } finally {
                                group.shutdownGracefully();
                            }
                        });
                try {
                    field.set(bean, proxy);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return bean;
    }
}

